package com.sprite.framework.entity.condition;

import java.util.Collection;

import com.sprite.framework.entity.EntityException;
import com.sprite.framework.entity.DataScriptStatement;
import com.sprite.framework.entity.model.ModelEntityView;
import com.sprite.framework.entity.script.EntityView;
import com.sprite.utils.UtilCollection;
import com.sprite.utils.UtilMisc;


/**
 * @author Jack
 *
 * @param <L> 左侧值
 * @param <R> 右侧值
 * @param <T> 操作结果类型
 */
public abstract class EntityOperator<L, R, T> {
	public static final int ID_EQUALS = 1;
	public static final int ID_NOT_EQUAL = 2;
	public static final int ID_LESS_THAN = 3;
	public static final int ID_GREATER_THAN = 4;
	public static final int ID_LESS_THAN_EQUAL_TO = 5;
	public static final int ID_GREATER_THAN_EQUAL_TO = 6;
	public static final int ID_IN = 7;
	public static final int ID_BETWEEN = 8;
	public static final int ID_NOT = 9;
	public static final int ID_AND = 10;
	public static final int ID_OR = 11;
	public static final int ID_LIKE = 12;
	public static final int ID_NOT_IN = 13;
	public static final int ID_NOT_LIKE = 14;
	public static final int ID_IS_NULL = 15;
	public static final int ID_NOT_NULL = 16;

	private int idInt;
	private String codeString;

	protected EntityOperator(int id, String code){
		idInt = id;
		codeString = code;
	}

	public String getCode(){
		if(codeString == null){
			return "null";
		}else{
			return codeString;
		}
	}

	public abstract void makeScript(DataScriptStatement script,L l, R r, ModelEntityView modelViewEntity) throws EntityException;

	@Override
	public String toString() {
		return codeString;
	}

	@Override
	public int hashCode() {
		return this.codeString.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof EntityOperator<?,?,?>) {
			EntityOperator<?,?,?> otherOper = UtilMisc.cast(obj);
			return this.idInt == otherOper.idInt;
		}
		return false;
	}

	public static final EntityComparisonOperator<?,?> EQUALS = new EntityComparisonOperator<Object, Object>(ID_EQUALS, "=") {};
	public static final EntityComparisonOperator<?,?> NOT_EQUALS = new EntityComparisonOperator<Object, Object>(ID_NOT_EQUAL, "!=") {};
	public static final EntityComparisonOperator<?,?> LESS_THAN = new EntityComparisonOperator<Object, Object>(ID_LESS_THAN, "<") {};
	public static final EntityComparisonOperator<?,?> GREATER_THAN = new EntityComparisonOperator<Object, Object>(ID_GREATER_THAN, ">") {};
	public static final EntityComparisonOperator<?,?> LESS_THAN_EQUAL_TO = new EntityComparisonOperator<Object, Object>(ID_LESS_THAN_EQUAL_TO, "<=") {};
	public static final EntityComparisonOperator<?,?> GREATER_THAN_EQUAL_TO = new EntityComparisonOperator<Object, Object>(ID_GREATER_THAN_EQUAL_TO, ">=") {};
	public static final EntityComparisonOperator<?,?> NOT = new EntityComparisonOperator<Object, Object>(ID_NOT, "NOT") {};
	public static final EntityComparisonOperator<?,?> LIKE = new EntityComparisonOperator<Object, Object>(ID_LIKE, "LIKE") {
		@Override
		public void makeScript(DataScriptStatement script,Object l, Object r, ModelEntityView modelViewEntity) throws EntityException{
			if(r == null){
				return;
			}

			if(r instanceof CharSequence){
				String val = String.valueOf(r);
				super.makeScript(script, l, "%"+val+"%", modelViewEntity);
			}else{
				throw new EntityException("EntityOption.Link only suport String");
			}
		}
	};
	public static final EntityComparisonOperator<?,?> NOT_LIKE = new EntityComparisonOperator<Object, Object>(ID_NOT_LIKE, "NOT LIKE") {};
	public static final EntityComparisonOperator<?,?> IS_NULL = new EntityComparisonOperator<Object, Object>(ID_IS_NULL, "IS") {};
	public static final EntityComparisonOperator<?,?> NOT_NULL = new EntityComparisonOperator<Object, Object>(ID_NOT_NULL, "IS NOT") {};

	public static final CollectionEntityComparisonOperator<?> IN = new CollectionEntityComparisonOperator<MultiOperateValue>(ID_IN, "IN") {

		@Override
		public void makeScript(DataScriptStatement script, String l,
							   MultiOperateValue r, ModelEntityView modelViewEntity) throws EntityException{

			if(l == null){
				throw new IllegalArgumentException("The Operator [IN] must have left");
			}

			script.append(" ");

			{
				String fieldName = l;
				if(modelViewEntity != null){
					script.append(modelViewEntity.resolveFieldPath(fieldName));
				}else{
					script.append(fieldName);
				}
			}

			script.append(" ");
			script.append(getCode());
			script.append("(");

			if(EntityView.class.isInstance(r)){
				r.makeStatement(script, null);
			}else{
				r.makeStatement(script, modelViewEntity);
			}

			script.append(")");

		}};
	public static final CollectionEntityComparisonOperator<?> NOT_IN = new CollectionEntityComparisonOperator<MultiOperateValue>(ID_NOT_IN, "NOT IN") {

		@Override
		public void makeScript(DataScriptStatement script, String l,
							   MultiOperateValue r, ModelEntityView modelViewEntity) throws EntityException{

			if(l == null){
				throw new IllegalArgumentException("The Operator [NOT IN] must have left");
			}

			script.append(" ");

			{
				String fieldName = l;
				if(modelViewEntity != null){
					script.append(modelViewEntity.resolveFieldPath(fieldName));
				}else{
					script.append(fieldName);
				}
			}

			script.append(" ");
			script.append(getCode());
			script.append("(");

			r.makeStatement(script, modelViewEntity);

			script.append(")");

		}};

	public static final CollectionEntityComparisonOperator<?> BETWEEN = new CollectionEntityComparisonOperator<CollectionOperateValue>(ID_BETWEEN, "BETWEEN") {

		@Override
		public void makeScript(DataScriptStatement script, String l,
							   CollectionOperateValue v, ModelEntityView modelViewEntity) throws EntityException{
			if(l == null){
				throw new IllegalArgumentException("The Operator [BETWEEN] must have left");
			}

			Collection<?> r = v.getValues();

			if(r == null || r.size() != 2){
				throw new IllegalArgumentException("The Operator [BETWEEN] must have two values");
			}

			script.append(" ");
			{
				String fieldName = l;
				if(modelViewEntity != null){
					script.append(modelViewEntity.resolveFieldPath(fieldName));
				}else{
					script.append(fieldName);
				}
			}
			script.append(" ");

			script.append(getCode());

			Object obj = UtilCollection.at(r, 0);
			if(obj instanceof NoParameterized){
				NoParameterized p = (NoParameterized)obj;
				script.append(p.safeString());
			}else{
				script.append(" ? ");
				script.addParam(obj);
			}

			script.append(" AND ");


			obj = UtilCollection.at(r, 1);
			if(obj instanceof NoParameterized){
				NoParameterized p = (NoParameterized)obj;
				script.append(p.safeString());
			}else{
				script.append(" ? ");
				script.addParam(obj);
			}
		}};

	public static final EntityJoinOperator AND = new EntityJoinOperator(ID_AND, "AND");
	public static final EntityJoinOperator OR = new EntityJoinOperator(ID_OR, "OR");
}
